﻿using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
using NHibernate;
using NHibernate.Expression;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ProjectZero.Model
{
    [DataObject(true)]
    public class RecordBase<T> : ActiveRecordBase where T : class
    {
        public static bool ConnOK()
        {
            return (ConnectionState.Open == ConnectionState.Open);
        }

        public static string PrepareLike(string s)
        {

            if (string.IsNullOrEmpty(s)) return s;
            else return string.Format("%{0}%", s);
        }

        public static T[] FindAll()
        {
            return ActiveRecordMediator<T>.FindAll();
        }

        public static T Find(long id)
        {
            return ActiveRecordMediator<T>.FindByPrimaryKey(id, false);
        }

        public static T Find(Int16 id)
        {
            return ActiveRecordMediator<T>.FindByPrimaryKey(id, false);
        }

        public static T Find(Int32 id)
        {
            return ActiveRecordMediator<T>.FindByPrimaryKey(id, false);
        }

        public static T Find(decimal id)
        {
            return ActiveRecordMediator<T>.FindByPrimaryKey(id, false);
        }

        public static T FindFirst(string order)
        {
            return FindFirst(ToHibernateOrder(order.Split(',')));
        }

        public static T FindFirst(string order, params ICriterion[] criterias)
        {
            return FindFirst(ToHibernateOrder(order.Split(',')), criterias);
        }

        public static T FindFirst(Order[] orders, params ICriterion[] criterias)
        {
            return ActiveRecordMediator<T>.FindFirst(orders, criterias);
        }

        public static T FindFirst(params ICriterion[] criterias)
        {
            return ActiveRecordMediator<T>.FindFirst(criterias);
        }

        public static T FindOne(params ICriterion[] criterias)
        {
            return ActiveRecordMediator<T>.FindOne(criterias);
        }

        public static T[] FindAll(string order)
        {
            return FindAll(order.Split(','));
        }

        public static T[] FindAll(params string[] order)
        {
            return ActiveRecordMediator<T>.FindAll(ToHibernateOrder(order));
        }

        public static T[] FindAll(params ICriterion[] criterion)
        {
            return ActiveRecordMediator<T>.FindAll(criterion);
        }

        public static T[] FindAll(string order, params ICriterion[] criterion)
        {
            return ActiveRecordMediator<T>.FindAll(ToHibernateOrder(order.Split(',')), criterion);
        }

        public static T[] FindAll(Order[] orders, params ICriterion[] criterion)
        {
            return ActiveRecordMediator<T>.FindAll(orders, criterion);
        }

        public new virtual void Delete()
        {
            ActiveRecordMediator.Delete(this);
        }

        public static void Save(T instance)
        {
            ActiveRecordMediator<T>.Save(instance);
        }

        internal static IList ExecuteNativeSQL(Type type, string sql, string alias)
        {
            IList retval = null;

            ISessionFactoryHolder holder = ActiveRecordMediator.GetSessionFactoryHolder();
            ISession session = holder.CreateSession(type);
            try
            {
                retval = session.CreateSQLQuery(sql, alias, type).List();
            }
            finally
            {
                holder.ReleaseSession(session);
            }

            return retval;
        }

        internal static bool ExecuteQuery(Type type, string sql)
        {
            bool retorno = false;

            IQuery query = null;
            ISQLQuery sqlQuery = null;

            ISessionFactoryHolder holder = ActiveRecordMediator.GetSessionFactoryHolder();
            ISession session = holder.CreateSession(type);
            try
            {
                query = session.CreateQuery(sql);
                sqlQuery = session.CreateSQLQuery(sql);
                sqlQuery.SetForceCacheRefresh(true);
                session.Flush();
                retorno = true;
            }
            finally
            {
                holder.ReleaseSession(session);
            }

            return retorno;
        }

        internal static int ExecuteNativeNonNativeSQL(Type type, string sql)
        {
            int retorno = 0;

            ISessionFactoryHolder holder = ActiveRecordMediator.GetSessionFactoryHolder();
            ISession session = holder.CreateSession(type);
            try
            {
                IDbConnection con = session.Connection;
                Type conType = con.GetType();
                string conTypeName = conType.FullName;

                if (!conTypeName.EndsWith("Connection"))
                    throw new Exception("Unable to interpret connection type name: " + conTypeName);

                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = sql;

                IDisposable cmdDisposable = cmd as IDisposable;
                try
                {
                    retorno = cmd.ExecuteNonQuery();
                }
                finally
                {
                    if (cmdDisposable != null)
                        cmdDisposable.Dispose();
                }
            }
            finally
            {
                holder.ReleaseSession(session);
            }

            return retorno;
        }

        internal static DataSet ExecuteNativeSQL(Type type, string sql)
        {
            DataSet ds = null;

            ISessionFactoryHolder holder = ActiveRecordMediator.GetSessionFactoryHolder();
            ISession session = holder.CreateSession(type);
            try
            {
                ds = new DataSet();
                IDbConnection con = session.Connection;
                Type conType = con.GetType();
                string conTypeName = conType.FullName;

                if (!conTypeName.EndsWith("Connection"))
                    throw new Exception("Unable to interpret connection type name: " + conTypeName);

                string adapterTypeName = conTypeName.Substring(0, conTypeName.Length - 10) + "DataAdapter";
                IDbDataAdapter adapter = conType.Assembly.CreateInstance(adapterTypeName) as IDbDataAdapter;

                if (adapter == null)
                    throw new Exception("Unable to load IDbDataAdapter: " + adapterTypeName);
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = sql;
                adapter.SelectCommand = cmd;

                IDisposable adapterDisposable = adapter as IDisposable;
                IDisposable cmdDisposable = cmd as IDisposable;
                try
                {
                    adapter.Fill(ds);
                }
                finally
                {
                    if (adapterDisposable != null)
                        adapterDisposable.Dispose();
                    if (cmdDisposable != null)
                        cmdDisposable.Dispose();
                }
            }
            finally
            {
                holder.ReleaseSession(session);
            }

            return ds;
        }

        internal static DataTable GenericSelect(Type type, string sql)
        {
            DataTable dt = new DataTable();

            DataSet ds = ExecuteNativeSQL(type, sql);

            if (ds.Tables.Count > 0)
                dt.Merge(ds.Tables[0]);

            return dt;
        }

        internal static Order[] ToHibernateOrder(string[] orders)
        {
            if (orders == null || orders.Length == 0)
                return new Order[0];

            ArrayList al = new ArrayList();
            for (int i = 0; i < orders.Length; i++)
            {
                Order o = ToHibernateOrder(orders[i]);
                if (o != null) al.Add(o);
            }

            return (Order[])al.ToArray(typeof(Order));
        }

        internal static Order ToHibernateOrder(string order)
        {
            string[] splitted = order.Split(' ');
            bool asc = true;
            if (splitted.Length == 2)
                asc = String.Compare("DESC", splitted[1].Trim(), true) != 0;
            else
            {
                if (string.IsNullOrEmpty(splitted[0]) ||
                    string.Compare(splitted[0].Trim(), "asc", true) == 0 ||
                    string.Compare(splitted[0].Trim(), "desc", true) == 0)
                    return null;
            }
            return new Order(splitted[0], asc);
        }

        public new virtual void Save()
        {
            /*if (this is IAuditavel)
                ((IAuditavel)this).AuditData = DateTime.Now;*/

            ActiveRecordMediator.Save(this);
        }
    }
}
